{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pipeline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.optim as optim\n",
    "import torch.nn as nn\n",
    "\n",
    "from datasets import load_dataset\n",
    "from transformers import pipeline\n",
    "\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from tqdm import *\n",
    "import sys\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "warnings.simplefilter('ignore')\n",
    "\n",
    "# 判断可用的设备是 CPU 还是 GPU，并将模型移动到对应的计算资源设备上\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "No model was supplied, defaulted to distilbert-base-uncased-finetuned-sst-2-english and revision af0f99b (https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english).\n",
      "Using a pipeline without specifying a model name and revision in production is not recommended.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'label': 'POSITIVE', 'score': 0.9998773336410522}\n",
      "{'label': 'NEGATIVE', 'score': 0.9997857213020325}\n"
     ]
    }
   ],
   "source": [
    "#文本分类\n",
    "classifier = pipeline(\"sentiment-analysis\")\n",
    "\n",
    "result = classifier(\"This is a great movie. I enjoyed it a lot!\")[0]\n",
    "print(result)\n",
    "\n",
    "result = classifier(\"This movie is so bad, I almost fell asleep.\")[0]\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据集查看"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Found cached dataset imdb (C:/Users/Jack/.cache/huggingface/datasets/imdb/plain_text/1.0.0/d613c88cf8fa3bab83b4ded3713f1f74830d1100e171db75bbddb80b3345c9c0)\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "cbb4608d5f154569b93dda1c30d260d3",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/3 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'text': 'I rented I AM CURIOUS-YELLOW from my video store because of all the controversy that surrounded it when it was first released in 1967. I also heard that at first it was seized by U.S. customs if it ever tried to enter this country, therefore being a fan of films considered \"controversial\" I really had to see this for myself.<br /><br />The plot is centered around a young Swedish drama student named Lena who wants to learn everything she can about life. In particular she wants to focus her attentions to making some sort of documentary on what the average Swede thought about certain political issues such as the Vietnam War and race issues in the United States. In between asking politicians and ordinary denizens of Stockholm about their opinions on politics, she has sex with her drama teacher, classmates, and married men.<br /><br />What kills me about I AM CURIOUS-YELLOW is that 40 years ago, this was considered pornographic. Really, the sex and nudity scenes are few and far between, even then it\\'s not shot like some cheaply made porno. While my countrymen mind find it shocking, in reality sex and nudity are a major staple in Swedish cinema. Even Ingmar Bergman, arguably their answer to good old boy John Ford, had sex scenes in his films.<br /><br />I do commend the filmmakers for the fact that any sex shown in the film is shown for artistic purposes rather than just to shock people and make money to be shown in pornographic theaters in America. I AM CURIOUS-YELLOW is a good film for anyone wanting to study the meat and potatoes (no pun intended) of Swedish cinema. But really, this film doesn\\'t have much of a plot.', 'label': 0}\n",
      "{'text': 'The story centers around Barry McKenzie who must go to England if he wishes to claim his inheritance. Being about the grossest Aussie shearer ever to set foot outside this great Nation of ours there is something of a culture clash and much fun and games ensue. The songs of Barry McKenzie(Barry Crocker) are highlights.', 'label': 1}\n"
     ]
    }
   ],
   "source": [
    "from datasets import load_dataset\n",
    "\n",
    "imdb_dataset = load_dataset('imdb')# 加载imdb数据集\n",
    "print(imdb_dataset['train'][0]) # 查看第一条数据\n",
    "print(imdb_dataset['train'][-1]) # 查看最后一条数据"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Found cached dataset imdb (C:/Users/Jack/.cache/huggingface/datasets/imdb/plain_text/1.0.0/d613c88cf8fa3bab83b4ded3713f1f74830d1100e171db75bbddb80b3345c9c0)\n",
      "Found cached dataset imdb (C:/Users/Jack/.cache/huggingface/datasets/imdb/plain_text/1.0.0/d613c88cf8fa3bab83b4ded3713f1f74830d1100e171db75bbddb80b3345c9c0)\n"
     ]
    }
   ],
   "source": [
    "#定义数据集\n",
    "class Dataset(torch.utils.data.Dataset):\n",
    "    def __init__(self, split):\n",
    "        self.dataset = load_dataset(path='imdb', split=split)\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.dataset)\n",
    "\n",
    "    def __getitem__(self, i):\n",
    "        text = self.dataset[i]['text']\n",
    "        label = self.dataset[i]['label']\n",
    "        return text, label\n",
    "\n",
    "\n",
    "train_dataset = Dataset('train')\n",
    "test_dataset = Dataset('test')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25000 25000\n"
     ]
    }
   ],
   "source": [
    "print(len(train_dataset), len(test_dataset))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 词元化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "BertTokenizerFast(name_or_path='bert-base-cased', vocab_size=28996, model_max_length=512, is_fast=True, padding_side='right', truncation_side='right', special_tokens={'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'})"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from transformers import AutoTokenizer\n",
    "\n",
    "#加载Tokenizer\n",
    "tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')\n",
    "tokenizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数据集处理函数\n",
    "def collate_fn(data):\n",
    "    sents = [i[0] for i in data]\n",
    "    labels = [i[1] for i in data]\n",
    "\n",
    "    #编码\n",
    "    data = tokenizer.batch_encode_plus(batch_text_or_text_pairs=sents,\n",
    "                                   truncation=True,\n",
    "                                   padding='max_length',\n",
    "                                   max_length=500,\n",
    "                                   return_tensors='pt',\n",
    "                                   return_length=True)\n",
    "\n",
    "    #input_ids:编码之后的数字\n",
    "    #attention_mask:是补零的位置是0,其他位置是1\n",
    "    input_ids = data['input_ids']\n",
    "    attention_mask = data['attention_mask']\n",
    "    token_type_ids = data['token_type_ids']\n",
    "    labels = torch.LongTensor(labels)\n",
    "\n",
    "    return input_ids, attention_mask, token_type_ids, labels\n",
    "\n",
    "\n",
    "#定义数据加载器\n",
    "train_loader = torch.utils.data.DataLoader(dataset=train_dataset,\n",
    "                                     batch_size=32,\n",
    "                                     collate_fn=collate_fn,\n",
    "                                     shuffle=True)\n",
    "\n",
    "test_loader = torch.utils.data.DataLoader(dataset=test_dataset,\n",
    "                                              batch_size=32,\n",
    "                                              collate_fn=collate_fn,\n",
    "                                              shuffle=True)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 建立模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Some weights of the model checkpoint at bert-base-cased were not used when initializing BertModel: ['cls.seq_relationship.weight', 'cls.predictions.decoder.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.weight']\n",
      "- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
      "- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n"
     ]
    }
   ],
   "source": [
    "from transformers import BertModel\n",
    "\n",
    "#加载预训练bert模型\n",
    "pretrained = BertModel.from_pretrained('bert-base-cased').to(device)\n",
    "\n",
    "#不训练,不需要计算梯度\n",
    "for param in pretrained.parameters():\n",
    "    param.requires_grad_(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义下游任务模型\n",
    "class Model(torch.nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.fc = torch.nn.Linear(768, 2)\n",
    "\n",
    "    def forward(self, input_ids, attention_mask, token_type_ids):\n",
    "        with torch.no_grad():\n",
    "            out = pretrained(input_ids=input_ids,\n",
    "                       attention_mask=attention_mask,\n",
    "                       token_type_ids=token_type_ids)\n",
    "        out = self.fc(out.last_hidden_state[:, 0]) # 最后一层隐藏层作为输入\n",
    "        out = out.softmax(dim=1)\n",
    "        return out\n",
    "\n",
    "model = Model().to(device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义训练器\n",
    "class Trainer:\n",
    "    def __init__(self, model, train_loader, valid_loader):\n",
    "        # 初始化训练数据集和验证数据集的dataloader\n",
    "        self.train_loader = train_loader\n",
    "        self.valid_loader = valid_loader\n",
    "        \n",
    "        self.device = device\n",
    "        self.model = model.to(self.device)\n",
    "        \n",
    "        # 定义优化器、损失函数和学习率调度器\n",
    "        self.optimizer = optim.AdamW(self.model.parameters(), lr=0.001)\n",
    "        self.criterion = nn.CrossEntropyLoss()\n",
    "        self.scheduler = optim.lr_scheduler.ExponentialLR(self.optimizer, gamma=0.95)\n",
    "        \n",
    "        # 记录训练过程中的损失和验证过程中的准确率\n",
    "        self.train_losses = []\n",
    "        self.val_accuracy = []\n",
    "    \n",
    "    def train(self, num_epochs):\n",
    "        # tqdm用于显示进度条并评估任务时间开销\n",
    "        for epoch in tqdm(range(num_epochs), file=sys.stdout):\n",
    "            # 记录损失值\n",
    "            total_loss = 0\n",
    "\n",
    "            # 批量训练\n",
    "            self.model.train()\n",
    "            \n",
    "            for input_ids, attention_mask, token_type_ids, labels in train_loader:\n",
    "                # 预测、损失函数、反向传播\n",
    "                self.optimizer.zero_grad()\n",
    "                outputs = self.model(input_ids=input_ids.to(self.device), attention_mask=attention_mask.to(self.device), token_type_ids=token_type_ids.to(self.device)).to(self.device)\n",
    "                loss = self.criterion(outputs, labels.to(self.device))\n",
    "                loss.backward()\n",
    "                self.optimizer.step()\n",
    "                total_loss += loss.item()\n",
    "            \n",
    "            # 更新优化器的学习率\n",
    "            self.scheduler.step()\n",
    "            # 计算验证集的准确率\n",
    "            accuracy = self.validate()\n",
    "            \n",
    "            # 记录训练集损失和验证集准确率\n",
    "            self.train_losses.append(total_loss)\n",
    "            self.val_accuracy.append(accuracy)\n",
    "            \n",
    "            # 打印中间值\n",
    "            tqdm.write(\"Epoch: {0} Loss: {1} Acc: {2}\".format(\n",
    "                epoch, self.train_losses[-1], self.val_accuracy[-1]))\n",
    "    \n",
    "    def validate(self):\n",
    "        # 测试模型，不计算梯度\n",
    "        self.model.eval()\n",
    "        \n",
    "        # 记录总数和预测正确数\n",
    "        total = 0\n",
    "        correct = 0\n",
    "        \n",
    "        with torch.no_grad():\n",
    "            for input_ids, attention_mask, token_type_ids, labels in self.valid_loader:\n",
    "                outputs = self.model(input_ids=input_ids.to(self.device), attention_mask=attention_mask.to(self.device), token_type_ids=token_type_ids.to(self.device)).to(self.device)\n",
    "                # 记录验证集总数和预测正确数\n",
    "                total += labels.size(0)\n",
    "                correct += (outputs.argmax(1) == labels.to(self.device)).sum().item()\n",
    "        \n",
    "        # 返回准确率\n",
    "        accuracy = correct / total\n",
    "        return accuracy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模型训练和验证"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 0 Loss: 434.53685945272446 Acc: 0.7914532650448144\n",
      "Epoch: 1 Loss: 402.5158587694168 Acc: 0.8007762483994878\n",
      "Epoch: 2 Loss: 393.65125730633736 Acc: 0.801576504481434\n",
      "Epoch: 3 Loss: 389.8109784722328 Acc: 0.8057378361075545\n",
      "Epoch: 4 Loss: 386.33617067337036 Acc: 0.8108994878361075\n",
      "Epoch: 5 Loss: 383.81841921806335 Acc: 0.810699423815621\n",
      "Epoch: 6 Loss: 382.1445892751217 Acc: 0.8136603713188221\n",
      "Epoch: 7 Loss: 380.45632472634315 Acc: 0.8143806017925737\n",
      "Epoch: 8 Loss: 379.21452555060387 Acc: 0.8153409090909091\n",
      "Epoch: 9 Loss: 378.1194171011448 Acc: 0.817381562099872\n",
      "Epoch: 10 Loss: 377.32714772224426 Acc: 0.8139004481434059\n",
      "Epoch: 11 Loss: 376.20741629600525 Acc: 0.8183418693982074\n",
      "Epoch: 12 Loss: 375.6312824189663 Acc: 0.8190220870678617\n",
      "Epoch: 13 Loss: 374.8075669705868 Acc: 0.8184619078104993\n",
      "Epoch: 14 Loss: 374.4073523283005 Acc: 0.819182138284251\n",
      "Epoch: 15 Loss: 373.81411695480347 Acc: 0.8206626120358514\n",
      "Epoch: 16 Loss: 373.27428355813026 Acc: 0.819822343149808\n",
      "Epoch: 17 Loss: 372.8720281124115 Acc: 0.8218229833546735\n",
      "Epoch: 18 Loss: 372.5892143249512 Acc: 0.8220630601792573\n",
      "Epoch: 19 Loss: 372.2855137884617 Acc: 0.8201824583866837\n",
      "Epoch: 20 Loss: 371.9072094857693 Acc: 0.8219030089628682\n",
      "Epoch: 21 Loss: 371.6156429052353 Acc: 0.8228233034571063\n",
      "Epoch: 22 Loss: 371.4782713353634 Acc: 0.8238636363636364\n",
      "Epoch: 23 Loss: 371.2345593869686 Acc: 0.8237836107554417\n",
      "Epoch: 24 Loss: 370.84895837306976 Acc: 0.8238236235595391\n",
      "Epoch: 25 Loss: 370.6026189625263 Acc: 0.8246638924455826\n",
      "Epoch: 26 Loss: 370.38798823952675 Acc: 0.8223031370038413\n",
      "Epoch: 27 Loss: 370.2933270037174 Acc: 0.8235835467349552\n",
      "Epoch: 28 Loss: 370.0269486308098 Acc: 0.8250240076824584\n",
      "Epoch: 29 Loss: 369.9317018389702 Acc: 0.8237435979513444\n",
      "100%|██████████| 30/30 [5:12:51<00:00, 625.71s/it]\n"
     ]
    }
   ],
   "source": [
    "# 创建一个 Trainer 类的实例\n",
    "trainer = Trainer(model, train_loader, test_loader)\n",
    "# 训练模型，迭代 30 个周期\n",
    "trainer.train(num_epochs = 30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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ruw3iePtVNnZ2XrooYf0/vvv6/3339eVV16pa6+9NhhGwnasGTAMwzAuvPBC44477uiwbvz48cb3v/99kyqKfD/84Q+NKVOmmF1GvyLJWL9+ffDv1tZWIyMjw3jooYeC65qbmw2Xy2X86le/MqHCyHTqfjMMw7j11luNa6+91pR6+ovq6mpDkrF582bDMDjeuurU/WYYHG9dlZSUZPzmN78J67HGiIokn8+n4uJizZ49u8P62bNna+vWrSZV1T/s3btXWVlZGjVqlL7xjW9o3759ZpfUr+zfv19VVVUdjj2n06nLL7+cY68LNm3apKFDh+r888/X7bffrurqarNLiihut1uSlJycLInjratO3W/tON7OLBAIqKioSA0NDZo+fXpYjzWCiqSjR48qEAgoPT29w/r09HRVVVWZVFXku+iii/T73/9ef/vb3/T000+rqqpKM2bM0LFjx8wurd9oP7449kI3d+5c/eEPf9Cbb76pRx55RNu2bdOVV14pr9drdmkRwTAM3Xvvvbr00kuVm5srieOtKzrbbxLH25mUlpYqPj5eTqdTd9xxh9avX6+JEyeG9Vjrl3dP7i0Wi6XD34ZhnLYOn5s7d27w98mTJ2v69Ok677zztHbtWt17770mVtb/cOyF7oYbbgj+npubq2nTpmnEiBF65ZVXdN1115lYWWS466679MEHH2jLli2nPcbxdmZn2m8cb50bN26cSkpKdPz4cT333HO69dZbtXnz5uDj4TjWGFGRlJqaKqvVelrKq66uPi0N4szi4uI0efJk7d271+xS+o32q6Q49nouMzNTI0aM4PiTdPfdd+vFF1/UW2+9pezs7OB6jrezO9N+6wzHWxuHw6ExY8Zo2rRpKiws1JQpU/SLX/wirMcaQUVtOzo/P18bN27ssH7jxo2aMWOGSVX1P16vVx9++KEyMzPNLqXfGDVqlDIyMjocez6fT5s3b+bYC9GxY8dUVlY2qI8/wzB011136fnnn9ebb76pUaNGdXic461z59pvneF465xhGPJ6veE91sI00bffKyoqMux2u7F69Wpj9+7dRkFBgREXF2d89tlnZpcWsZYtW2Zs2rTJ2Ldvn/Hee+8Z8+fPNxISEthnp6irqzN27Nhh7Nixw5BkPProo8aOHTuMAwcOGIZhGA899JDhcrmM559/3igtLTVuvPFGIzMz0/B4PCZXbq6z7be6ujpj2bJlxtatW439+/cbb731ljF9+nRj2LBhg3q/3XnnnYbL5TI2bdpkVFZWBpfGxsbgNhxvpzvXfuN469yKFSuMt99+29i/f7/xwQcfGPfdd58RFRVlvP7664ZhhO9YI6ic5Je//KUxYsQIw+FwGBdccEGHS9NwuhtuuMHIzMw07Ha7kZWVZVx33XXGrl27zC4r4rz11luGpNOWW2+91TCMtktGf/jDHxoZGRmG0+k0LrvsMqO0tNTcoiPA2fZbY2OjMXv2bCMtLc2w2+3G8OHDjVtvvdU4ePCg2WWbqrP9Jcn43e9+F9yG4+1059pvHG+d+9a3vhX8zkxLSzNmzpwZDCmGEb5jzWIYhtHNER4AAIBexRwVAAAQsQgqAAAgYhFUAABAxCKoAACAiEVQAQAAEYugAgAAIhZBBQAARCyCCgAAiFgEFQAAELEIKgAAIGIRVAAAQMQiqAAAgIj1/wMql45wxqS0YQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 使用Matplotlib绘制损失曲线图\n",
    "plt.plot(trainer.train_losses, label='loss')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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uN7WhjOliIiImofW69y0Zi1pnZtnPz+Gk+m38UmYPyb072jQthAREbVkzbrODemGIAjqMTd8LEVERKRbDDcGUFCiwB3OlCIiImoWDDcGkJrHmVJERETNheHGANQrE/ORFBERkc4x3BgAZ0oRERE1H4YbA1CvccNwQ0REpHMMN3rGmVJERETNi+FGzzhTioiIqHkx3OgZZ0oRERE1L4YbPeNMKSIioubFcKNnnClFRETUvBhu9IwzpYiIiJoXw40ecaYUERFR82O40aO/z5TycWbPDRERUXNguNGjv8+UshRzphQREVFzYLjRI86UIiIian4MN3pUM1OKg4mJiIiaD8ONHtXMlOI0cCIioubDcKMnnClFRESkHww3esKZUkRERPrBcKMnnClFRESkHww3esKZUkRERPrBcKMnnClFRESkHww3esKZUkRERPrBcKMHnClFRESkPww3elBYyplSRERE+sJwowc14204U4qIiKj5MdzoAWdKERER6Q/DjR7UrHHDmVJERETNj+FGD67mcqYUERGRvjDcNDPOlCIiItIvhptmxplSRERE+sVw08w4U4qIiEi/GG6aGWdKERER6RfDTTPjTCkiIiL9YrhpZpwpRUREpF8MN82IM6WIiIj0j+GmGXGmFBERkf4x3DQjzpQiIiLSv0aFmw0bNsDb2xsWFhYICAjAkSNH6i07ffp0iESiWlufPn3UZSorK7Fs2TL4+PjAwsIC/v7+2L9/v8Z5li5dWuscbm5ujWm+3nCmFBERkf5pHW62b9+OiIgILFy4EElJSQgJCcHo0aORkZFRZ/m1a9dCKpWqt8zMTLRr1w7PPPOMusyiRYvw+eefY926dUhJScGcOXMwYcIEJCUlaZyrT58+Gue6cOGCts3XK86UIiIi0j+tw83q1asxc+ZMzJo1C7169cKaNWvg6emJ6OjoOsvb29vDzc1NvZ0+fRp37tzBjBkz1GW+/vpr/N///R+eeOIJdOnSBS+//DJGjRqFjz/+WONcZmZmGudydnbWtvl6xZlSRERE+qdVuFEoFEhMTERoaKjG/tDQUCQkJDToHJs3b8bIkSPh5eWl3ieXy2FhYaFRztLSEvHx8Rr7UlNT4eHhAW9vb0yePBlpaWn3rUsul0Mmk2ls+sKZUkRERIahVbgpKCiAUqmEq6urxn5XV1fk5OQ88HipVIpffvkF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\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 使用Matplotlib绘制准确率曲线图\n",
    "plt.plot(trainer.val_accuracy, label='accuracy')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 直接finetune"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Found cached dataset imdb (C:/Users/Jack/.cache/huggingface/datasets/imdb/plain_text/1.0.0/d613c88cf8fa3bab83b4ded3713f1f74830d1100e171db75bbddb80b3345c9c0)\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "bb8074d8b57d4dfbadaf0ac51f69638e",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/3 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Loading cached processed dataset at C:\\Users\\Jack\\.cache\\huggingface\\datasets\\imdb\\plain_text\\1.0.0\\d613c88cf8fa3bab83b4ded3713f1f74830d1100e171db75bbddb80b3345c9c0\\cache-23dbfa0878e525bf.arrow\n",
      "Loading cached processed dataset at C:\\Users\\Jack\\.cache\\huggingface\\datasets\\imdb\\plain_text\\1.0.0\\d613c88cf8fa3bab83b4ded3713f1f74830d1100e171db75bbddb80b3345c9c0\\cache-9fcb76b56aad9b74.arrow\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Map:   0%|          | 0/50000 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Some weights of the model checkpoint at bert-base-cased were not used when initializing BertForSequenceClassification: ['cls.seq_relationship.weight', 'cls.predictions.decoder.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.weight']\n",
      "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
      "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n",
      "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-cased and are newly initialized: ['classifier.weight', 'classifier.bias']\n",
      "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      \n",
       "      <progress value='1250' max='1250' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      [1250/1250 15:07, Epoch 10/10]\n",
       "    </div>\n",
       "    <table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       " <tr style=\"text-align: left;\">\n",
       "      <th>Epoch</th>\n",
       "      <th>Training Loss</th>\n",
       "      <th>Validation Loss</th>\n",
       "      <th>Accuracy</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>No log</td>\n",
       "      <td>0.310240</td>\n",
       "      <td>0.866000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>2</td>\n",
       "      <td>No log</td>\n",
       "      <td>0.558114</td>\n",
       "      <td>0.878000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>3</td>\n",
       "      <td>No log</td>\n",
       "      <td>0.913532</td>\n",
       "      <td>0.827000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>4</td>\n",
       "      <td>0.290100</td>\n",
       "      <td>0.651401</td>\n",
       "      <td>0.887000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>5</td>\n",
       "      <td>0.290100</td>\n",
       "      <td>0.673469</td>\n",
       "      <td>0.891000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>6</td>\n",
       "      <td>0.290100</td>\n",
       "      <td>0.708808</td>\n",
       "      <td>0.896000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>7</td>\n",
       "      <td>0.290100</td>\n",
       "      <td>0.855462</td>\n",
       "      <td>0.889000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>8</td>\n",
       "      <td>0.016900</td>\n",
       "      <td>0.835452</td>\n",
       "      <td>0.889000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>9</td>\n",
       "      <td>0.016900</td>\n",
       "      <td>0.846489</td>\n",
       "      <td>0.890000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>10</td>\n",
       "      <td>0.016900</td>\n",
       "      <td>0.834561</td>\n",
       "      <td>0.895000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table><p>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 导入必要的库\n",
    "from datasets import load_dataset\n",
    "from transformers import AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer\n",
    "import numpy as np\n",
    "import evaluate\n",
    "\n",
    "# 加载数据集\n",
    "dataset = load_dataset(\"imdb\")\n",
    "\n",
    "# 加载 BERT 分词器\n",
    "tokenizer = AutoTokenizer.from_pretrained(\"bert-base-cased\")\n",
    "\n",
    "# 定义用于对输入文本进行分词的函数\n",
    "def tokenize_function(examples):\n",
    "    return tokenizer(examples[\"text\"], padding=\"max_length\", truncation=True)\n",
    "\n",
    "# 对数据集进行分词处理\n",
    "tokenized_datasets = dataset.map(tokenize_function, batched=True)\n",
    "\n",
    "# 从数据集中选择一小部分用于训练和测试\n",
    "small_train_dataset = tokenized_datasets[\"train\"].shuffle(seed=0).select(range(1000))\n",
    "small_eval_dataset = tokenized_datasets[\"test\"].shuffle(seed=0).select(range(1000))\n",
    "\n",
    "# 加载 BERT-base-cased 模型用于序列分类任务\n",
    "model = AutoModelForSequenceClassification.from_pretrained(\"bert-base-cased\", num_labels=2)\n",
    "\n",
    "# 加载准确率度量\n",
    "metric = evaluate.load(\"accuracy\")\n",
    "\n",
    "# 定义用于计算评估指标的函数\n",
    "def compute_metrics(eval_pred):\n",
    "    logits, labels = eval_pred\n",
    "    predictions = np.argmax(logits, axis=-1)\n",
    "    return metric.compute(predictions=predictions, references=labels)\n",
    "\n",
    "# 设置训练参数\n",
    "training_args = TrainingArguments(\n",
    "    output_dir='./results',\n",
    "    num_train_epochs=10, \n",
    "    evaluation_strategy=\"epoch\")\n",
    "\n",
    "# 创建一个 Trainer 实例\n",
    "trainer = Trainer(\n",
    "    model=model,\n",
    "    args=training_args,\n",
    "    train_dataset=small_train_dataset,\n",
    "    eval_dataset=small_eval_dataset,\n",
    "    compute_metrics=compute_metrics,\n",
    ")\n",
    "\n",
    "# 训练模型\n",
    "trainer.train()\n",
    "\n",
    "# 将训练好的模型保存到磁盘上\n",
    "model.save_pretrained('./results/imdb_model')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'label': 'LABEL_1', 'score': 0.9999207258224487}]\n",
      "[{'label': 'LABEL_0', 'score': 0.9998856782913208}]\n"
     ]
    }
   ],
   "source": [
    "# 加载模型\n",
    "model = AutoModelForSequenceClassification.from_pretrained('./results/imdb_model')\n",
    "\n",
    "# 创建pipeline\n",
    "classifier = pipeline('sentiment-analysis', model=model, tokenizer=tokenizer)\n",
    "\n",
    "# 测试模型\n",
    "result = classifier('This is a great movie. I enjoyed it a lot!')\n",
    "print(result)\n",
    "\n",
    "# 测试模型\n",
    "result = classifier('This movie is so bad, I almost fell asleep.')\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
